﻿//---------------------------------------------------------------------
// Copyright (c) 2007 Digerati Technologies, LLC.  All Rights Reserved.
//
// Release under the Microsoft Public License (Ms-PL).
//---------------------------------------------------------------------

using System;
using Microsoft.Win32;

namespace DigeratiTech.NDiagnostics
{
    /// <summary>
    /// This <see cref="DiagnosticTask"/> verifies registry keys and, optionally, values.
    /// </summary>
    public class RegistryTask : DiagnosticTask
    {
        #region Properties...

        /// <summary>
        /// Gets or sets the base registry key.
        /// </summary>
        /// <value>The base registry key.</value>
        /// <remarks>This is the base registry key.  E.g. HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, etc.</remarks>
        public string BaseKey { get; set; }

        /// <summary>
        /// Gets or sets the registry key to be verified.
        /// </summary>
        /// <value>The registry key to be verified.</value>
        /// <remarks>The specified path must exist for this task to succeed.</remarks>
        public string Key { get; set; }

        /// <summary>
        /// Gets or sets the name of a value to be verified.
        /// </summary>
        /// <value>The name of a value to be verified.</value>
        /// <remarks>A null version will cause the version check to be skipped.</remarks>
        public string ValueName { get; set; }

        /// <summary>
        /// Gets or sets the value to be verified.
        /// </summary>
        /// <value>The value to be verified.</value>
        public string Value { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether variables will be expanded when verifying registry values.
        /// </summary>
        /// <value><c>true</c> if variables are to be expanded when verifying registry values; otherwise, <c>false</c>.</value>
        public bool ExpandVariables { get; set; }

        #endregion

        #region Constructors...

        /// <summary>
        /// Initializes a new instance of the <see cref="RegistryTask"/> class.
        /// </summary>
        /// <param name="group">The <see cref="TaskGroup"/> this task belongs to.</param>
        /// <param name="name">The task name.</param>
        /// <param name="baseKey">The base key.</param>
        /// <param name="key">The registry key to be validated.</param>
        /// <param name="valueName">The name of a value to be verified (optional).</param>
        /// <param name="value">The value to be verified (if a valueName was specified).</param>
        /// <param name="expandVariables"><c>true</c> if variables are to be expanded when verifying registry values; otherwise, <c>false</c>.</param>
        public RegistryTask(TaskGroup group, string name, string baseKey, string key, string valueName, string value, bool expandVariables)
        {
            taskGroup = group;
            Name = name;
            BaseKey = baseKey;
            Key = key;
            ValueName = valueName;
            Value = value;
            ExpandVariables = expandVariables;
            Status = TaskStatus.NotRun;
        }

        #endregion

        #region Methods...

        /// <summary>
        /// Executes the task.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if the task is successful; otherwise, <c>false</c>.
        /// </returns>
        public override bool Execute()
        {
            Status = TaskStatus.Completed;
            Message = string.Empty;

            RegistryKey regKey;
            object regValue;

            // Get a reference to the correct base key
            switch (BaseKey.ToLower())
            {
                case "hkey_classes_root":
                regKey = Microsoft.Win32.Registry.ClassesRoot;
                break;

                case "hkey_current_config":
                regKey = Microsoft.Win32.Registry.CurrentConfig;
                break;

                case "hkey_current_user":
                regKey = Microsoft.Win32.Registry.CurrentUser;
                break;

                case "hkey_dyn_data":
                regKey = Microsoft.Win32.Registry.DynData;
                break;

                case "hkey_local_machine":
                regKey = Microsoft.Win32.Registry.LocalMachine;
                break;

                case "hkey_performance_data":
                regKey = Microsoft.Win32.Registry.PerformanceData;
                break;

                case "hkey_users":
                regKey = Microsoft.Win32.Registry.Users;
                break;

                default:
                throw new InvalidOperationException("You must specify a BaseKey for the RegistryTask.");
            }

            // Remove any leading backslashes prior to opening the key
            regKey = regKey.OpenSubKey(Key.TrimStart('\\'), false);

            // Check for the initial registry key (BaseKey\Key)
            if (regKey == null)
            {
                Status = TaskStatus.Error;
                Message += ((Message.Length > 0) ? "\n" : "") + string.Format("The registry key '{0} could not be opened.", BaseKey + "\\" + Key);
            }
            else
            {
                // If a value was specified, then check it as well
                if (!string.IsNullOrEmpty(ValueName))
                {
                    if (ExpandVariables)
                    {
                        regValue = regKey.GetValue(ValueName, null);
                    }
                    else
                    {
                        regValue = regKey.GetValue(ValueName, null, RegistryValueOptions.DoNotExpandEnvironmentNames);
                    }

                    if (regValue == null)
                    {
                        Status = TaskStatus.Error;
                        Message += ((Message.Length > 0) ? "\n" : "") + string.Format("The registry value '{0} does not exist.", ValueName);
                    }
                    else if (!regValue.ToString().Equals(Value, StringComparison.CurrentCultureIgnoreCase))
                    {
                        Status = TaskStatus.Error;
                        Message += ((Message.Length > 0) ? "\n" : "") + string.Format("The registry value should be set to '{0} but is set to {1}.", Value, regValue.ToString());
                    }
                }
            }

            return (Message.Length == 0);
        }

        /// <summary>
        /// Gets the specified subkey from the base key passed in.
        /// </summary>
        /// <param name="baseKey">The base key.</param>
        /// <param name="subKeyName">The name of the subkey to retrieve.</param>
        /// <returns>The registry subkey if it is found; otherwise, null.</returns>
        private RegistryKey GetSubKey(RegistryKey baseKey, string subKeyName)
        {
            RegistryKey subKey = null;

            foreach (string name in baseKey.GetSubKeyNames())
            {
                if (name.Equals(subKeyName, StringComparison.CurrentCultureIgnoreCase))
                {
                    subKey = baseKey.OpenSubKey(name, false);
                }
            }

            return subKey;
        }

        #endregion
    }
}
